package com.distribution.system.utils.convert;


import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.security.AccessController;
import java.util.*;

/**
 * @author huoliping
 * @description bean实例的copy
 * @date 2021-01-21 11:27
 **/
@Slf4j
public class BeanUtils {


    /**
     * 对象通过反射 按照名字进行 赋值
     *
     * @param sourceObject 源对象 如果属性名不相同 可在 sourceObject 中使用 Attribute 标记需要映射为 targetObject 对象中的那个属性名
     * @param targetObject 需要设置属性的对象
     * @param <T>          泛型
     * @return 被设置属性的对象
     */
    public static <T> T copyBean(Object sourceObject, T targetObject) {
        Field[] sourceFields = AccessController.doPrivileged(GetDeclaredFields.action(sourceObject));
        Field[] targetFields = AccessController.doPrivileged(GetDeclaredFields.action(targetObject));
        for (Field sourceField : sourceFields) {
            // 搜索依据属性名匹配的属性
            Field field = searchFieldByName(targetFields, sourceField);
            try {
                fromSourceFieldToTargetFieldCopy(sourceObject, sourceField, targetObject, field);
            } catch (IllegalAccessException e) {
                log.error("对象通过反射，按照名字进行赋值 出错：", e);
            }
        }
        return targetObject;
    }


    /**
     * 给定属性名进行匹配返回 Field
     *
     * @param searchedFields 被检索的Field集合
     * @param field          需要根据属性名或者 Attribute 注解值进行匹配的 属性
     * @return 匹配文件名的  Field
     */
    private static Field searchFieldByName(Field[] searchedFields, Field field) {

        Attribute annotation = field.getAnnotation(Attribute.class);
        String annotationValue = null;
        if (annotation != null) {
            annotationValue = annotation.attributeName();
        }
        String name = field.getName().intern();
        for (Field searchedField : searchedFields) {
            if (searchedField.getName().equals(name) | searchedField.getName().equals(annotationValue)) {
                return searchedField;
            }
        }
        return null;
    }

    /**
     * 从源对象的特定属性 复制值到 新对象的属性种
     *
     * @param sourceObject 源对象
     * @param sourceField  源对象属性
     * @param targetObject 需要设置属性的对象
     * @param targetField  需要设置属性的对象 的属性
     * @throws IllegalAccessException
     */
    private static void fromSourceFieldToTargetFieldCopy(Object sourceObject, Field sourceField, Object targetObject, Field targetField) throws IllegalAccessException {
        if (null != targetField) {
            sourceField.setAccessible(true);
            targetField.setAccessible(true);
            targetField.set(targetObject, sourceField.get(sourceObject));
        }
    }


    /**
     * 根据属性名获取属性值
     *
     * @param sourceObject  源对象
     * @param attributeName 属性名称
     * @return 属性值
     */
    public static Object getAttributeValueByName(Object sourceObject, String attributeName) {
        Object attributeValue = null;
        try {
            Field declaredField = sourceObject.getClass().getDeclaredField(attributeName);
            declaredField.setAccessible(true);
            attributeValue = declaredField.get(sourceObject);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return attributeValue;
    }


    /**
     * 复制 bean 集合 给定class 返回 class 类型的 list 集合
     *
     * @param targetObject 需要转换的集合类型
     * @param sourceObject 源对象
     * @return 新的《T》类型集合对象
     */
    public static <T> List<T> copyBeanCollection(Class<T> targetObject, Collection<?> sourceObject) {
        ArrayList<T> newList = new ArrayList<>();
        if (sourceObject != null) {
            for (Object obj : sourceObject) {
                try {
                    if (obj != null) {
                        newList.add(copyBean(obj, targetObject.newInstance()));
                    }
                } catch (InstantiationException | IllegalAccessException e) {
                    log.error("对象通过反射赋值新的Collection，按照名字进行赋值 出错：", e);
                }
            }
        }
        return newList;
    }


    /**
     * 对象转换给定属性名称  和  连接 条件
     *
     * @param sourceObject             源对象获取信息的对象
     * @param targetObject             目标对象（需要操作的对象）
     * @param compareAttributeNames    对比哪些属性要相等 关联条件
     * @param assignmentAttributeNames 将哪些属性依据属性名称对拷贝
     */
    public static <T> void copyBeanCollectionByAttributes(List<?> sourceObjects, List<T> targetObjects,
                                                          String[] compareAttributeNames,
                                                          String... assignmentAttributeNames) {
        //遍历源对象
        for (Object sourceObject : sourceObjects) {
            HashMap<String, Object> attributes = new HashMap<>(1<<4);
            for (int i = 0; i < compareAttributeNames.length; i++) {
                String compareAttributeName = compareAttributeNames[i];
                Object compareAttributeValue = getAttributeValueByName(sourceObject, compareAttributeName);
                attributes.put(compareAttributeName, compareAttributeValue);
            }
            //根据属性值和属性名搜索匹配bean 并返回 bean 的索引
            List<Integer> targetIndexes = searchObjectByAttributeValueAndNameIndexes(targetObjects, attributes);
            for (Integer targetIndex : targetIndexes) {
                T t = targetObjects.get(targetIndex);
                setAttributeByAttributeNames(sourceObject, t, assignmentAttributeNames);
            }
        }
    }


    /**
     * 根据属性名，属性值 获取匹配的对象 返回匹配的 list 索引
     *
     * @param sourceObject
     * @param <T>
     * @return
     */
    private static <T> List<Integer> searchObjectByAttributeValueAndNameIndexes(List<T> sourceObject, Map<String, Object> map) {
        ArrayList<Integer> temp = new ArrayList<>();
        for (int i = 0; i < sourceObject.size(); i++) {
            if (sourceObjectIsExistOgjMaps(sourceObject.get(i), map)) {
                temp.add(i);
            }
        }
        return temp;
    }


    /**
     * 判断对象中是否存在 给定的键值对
     *
     * @param sourceObject 源对象
     * @param map          需匹配的属性名和属性值
     * @return 是否可以匹配上
     */
    @SneakyThrows
    static boolean sourceObjectIsExistOgjMaps(Object sourceObject, Map<String, Object> map) {
        int temp = 0;
        Field[] sourceFields = AccessController.doPrivileged(GetDeclaredFields.action(sourceObject));
        for (Field sourceField : sourceFields) {
            sourceField.setAccessible(true);
            String name = sourceField.getName();
            Object o = sourceField.get(sourceObject);
            for (Map.Entry<String, Object> stringObjectEntry : map.entrySet()) {
                String key = stringObjectEntry.getKey();
                Object value = stringObjectEntry.getValue();
                if (name.equals(key) & value.equals(o)) {
                    temp++;
                }
            }
        }
        return temp == map.size();
    }


    @SneakyThrows
    private static <T> void setAttributeByAttributeNames(Object sourceObject, Object targetObject, String... assignmentAttributeNames) {
        if (assignmentAttributeNames.length > 0) {
            for (String sourceAttributeName : assignmentAttributeNames) {
                //获取源对象的属性名和属性值
                Field declaredField = sourceObject.getClass().getDeclaredField(sourceAttributeName);
                declaredField.setAccessible(true);
                Object sourceAttributeValue = declaredField.get(sourceObject);
                Field declaredField1 = targetObject.getClass().getDeclaredField(sourceAttributeName);
                declaredField1.setAccessible(true);
                declaredField1.set(targetObject, sourceAttributeValue);
            }
        }
    }


    /**
     * 根据属性名，属性值 获取匹配的对象 返回匹配的 list 索引
     *
     * @param attributeName
     * @param attributeValue
     * @param sourceObject
     * @param <T>
     * @return
     */
    @Deprecated
    private static <T> ArrayList<Integer> getObjectByAttributeValueAndName(List<T> sourceObject, String attributeName, Object attributeValue) {
        // 遍历原对象
        ArrayList<Integer> sourceIndex = new ArrayList<>();

        for (int i = 0; i < sourceObject.size(); i++) {
            //获取每一个源对象
            T source = sourceObject.get(i);
            Object o = null;
            try {
                Field declaredField = source.getClass().getDeclaredField(attributeName);
                declaredField.setAccessible(true);
                o = declaredField.get(source);
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }
            // 判断属性值是否相等
            if (o.equals(attributeValue)) {
                // 将 sourceObject 的当前对象按照属性名 进行赋值
                sourceIndex.add(i);
            }
        }
        return sourceIndex;
    }


}
